package com.gitee.huanminabc.utils_tools.spring_base.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * 注解扫描工具类
 * 用于扫描Spring容器中指定注解标记的方法
 *
 * @author sby-plugin-common
 * @since 1.0.0
 */
@Slf4j
public class AnnotationScanUtils {

    /**
     * 扫描指定注解标记的方法
     *
     * @param annotationClass 要扫描的注解类型
     * @param processor       处理方法，参数为方法对象和注解对象
     * @param <T>            注解类型
     */
    public static <T extends Annotation> void scanAnnotatedMethods(
            Class<T> annotationClass,
            BiConsumer<Method, T> processor) {
        scanAnnotatedMethods(annotationClass, processor, null);
    }

    /**
     * 扫描指定注解标记的方法
     *
     * @param annotationClass 要扫描的注解类型
     * @param processor       处理方法，参数为方法对象和注解对象
     * @param filter          过滤器，用于过滤要处理的Bean
     * @param <T>            注解类型
     */
    public static <T extends Annotation> void scanAnnotatedMethods(
            Class<T> annotationClass,
            BiConsumer<Method, T> processor,
            Function<Object, Boolean> filter) {
        
        long start = System.currentTimeMillis();
        ApplicationContext applicationContext = SpringContextUtil.getApplicationContext();
        
        if (applicationContext == null) {
            log.warn("ApplicationContext未初始化，无法扫描注解");
            return;
        }

        String[] beanNames = applicationContext.getBeanNamesForType(Object.class, true, false);
        Stream<String> stream = Arrays.stream(beanNames)
                .filter(StringUtils::hasText)
                .parallel();

        final int[] processedCount = {0};
        final int[] annotatedCount = {0};

        stream.forEach(beanName -> {
            try {
                Object bean = applicationContext.getBean(beanName);
                
                // 应用过滤器
                if (filter != null && !filter.apply(bean)) {
                    return;
                }

                Class<?> userClass = AopUtils.getTargetClass(bean);
                Map<Method, T> annotated = processClass(userClass, annotationClass);
                
                if (!annotated.isEmpty()) {
                    annotated.forEach(processor);
                    annotatedCount[0] += annotated.size();
                }
                
                processedCount[0]++;
            } catch (Throwable e) {
                log.error("扫描Bean失败: {}", beanName, e);
            }
        });

        log.info("注解扫描完成: 处理Bean数={}, 找到注解方法数={}, 耗时={}ms", 
                processedCount, annotatedCount, System.currentTimeMillis() - start);
    }

    /**
     * 扫描指定注解标记的方法并收集结果
     *
     * @param annotationClass 要扫描的注解类型
     * @param collector       收集器，用于收集扫描结果
     * @param <T>            注解类型
     * @param <R>            结果类型
     * @return 收集的结果列表
     */
    public static <T extends Annotation, R> List<R> scanAnnotatedMethodsAndCollect(
            Class<T> annotationClass,
            Function<Map.Entry<Method, T>, R> collector) {
        
        List<R> results = new ArrayList<>();
        scanAnnotatedMethods(annotationClass, (method, annotation) -> {
            R result = collector.apply(new AbstractMap.SimpleEntry<>(method, annotation));
            if (result != null) {
                results.add(result);
            }
        });
        return results;
    }

    /**
     * 处理单个类，查找指定注解标记的方法
     *
     * @param clazz           要处理的类
     * @param annotationClass 要查找的注解类型
     * @param <T>            注解类型
     * @return 方法到注解的映射
     */
    private static <T extends Annotation> Map<Method, T> processClass(Class<?> clazz, Class<T> annotationClass) {
        return MethodIntrospector.selectMethods(clazz,
                (MethodIntrospector.MetadataLookup<T>) method ->
                        AnnotationUtils.findAnnotation(method, annotationClass));
    }

    /**
     * 扫描指定注解标记的方法，支持自定义扫描逻辑
     *
     * @param annotationClass 要扫描的注解类型
     * @param scanner         自定义扫描器
     * @param <T>            注解类型
     */
    public static <T extends Annotation> void scanWithCustomLogic(
            Class<T> annotationClass,
            CustomAnnotationScanner<T> scanner) {
        
        long start = System.currentTimeMillis();
        ApplicationContext applicationContext = SpringContextUtil.getApplicationContext();
        
        if (applicationContext == null) {
            log.warn("ApplicationContext未初始化，无法扫描注解");
            return;
        }

        String[] beanNames = applicationContext.getBeanNamesForType(Object.class, true, false);
        Stream<String> stream = Arrays.stream(beanNames)
                .filter(StringUtils::hasText)
                .parallel();

        final int[] processedCount = {0};
        final int[] annotatedCount = {0};

        stream.forEach(beanName -> {
            try {
                Object bean = applicationContext.getBean(beanName);
                
                if (scanner.shouldSkipBean(bean, beanName)) {
                    return;
                }

                Class<?> userClass = AopUtils.getTargetClass(bean);
                Map<Method, T> annotated = processClass(userClass, annotationClass);
                
                if (!annotated.isEmpty()) {
                    annotatedCount[0] += annotated.size();
                    scanner.processAnnotatedMethods(bean, beanName, annotated);
                }
                
                processedCount[0]++;
            } catch (Throwable e) {
                log.error("自定义扫描Bean失败: {}", beanName, e);
            }
        });

        log.info("自定义注解扫描完成: 处理Bean数={}, 找到注解方法数={}, 耗时={}ms", 
                processedCount, annotatedCount, System.currentTimeMillis() - start);
    }

    /**
     * 自定义注解扫描器接口
     *
     * @param <T> 注解类型
     */
    @FunctionalInterface
    public interface CustomAnnotationScanner<T extends Annotation> {
        
        /**
         * 处理找到的注解方法
         *
         * @param bean       Bean对象
         * @param beanName   Bean名称
         * @param annotated 方法到注解的映射
         */
        void processAnnotatedMethods(Object bean, String beanName, Map<Method, T> annotated);

        /**
         * 是否跳过某个Bean
         * 默认不跳过任何Bean
         *
         * @param bean     Bean对象
         * @param beanName Bean名称
         * @return true表示跳过，false表示处理
         */
        default boolean shouldSkipBean(Object bean, String beanName) {
            return false;
        }
    }
}
